#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2016-2019, Niklas Hauser
# Copyright (c) 2017, Fabian Greif
# Copyright (c) 2021, Christopher Durand
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------


def init(module):
    module.name = ":platform:rcc"
    module.description = "Reset and Clock Control (RCC)"

def prepare(module, options):
    if not options[":target"].has_driver("rcc:stm32*"):
        return False

    module.depends(":cmsis:device", ":utils", ":platform:clock", ":architecture:delay")
    # FIXME: Move Peripherals enum somewhere better
    module.depends(":platform:gpio")
    return True

def build(env):
    device = env[":target"]
    driver = device.get_driver("rcc")

    properties = {}
    properties["target"] = target = device.identifier
    properties["partname"] = device.partname
    properties["core"] = core = device.get_driver("core")["type"]

    if target["family"] in ["c0"]:
        properties["hsi_frequency"] = 48_000_000
        properties["lsi_frequency"] = 32_000
        properties["boot_frequency"] = 12_000_000
    elif target["family"] in ["f1", "f3"]:
        properties["hsi_frequency"] = 8_000_000
        properties["lsi_frequency"] = 40_000
        properties["boot_frequency"] = properties["hsi_frequency"]
    elif target["family"] in ["h7"]:
        properties["hsi_frequency"] = 64_000_000
        properties["lsi_frequency"] = 32_000
        properties["boot_frequency"] = properties["hsi_frequency"]
    elif target["family"] in ["l0", "l1"]:
        properties["hsi_frequency"] = 16_000_000
        properties["lsi_frequency"] = 37_000
        properties["msi_frequency"] = 2_097_000
        properties["boot_frequency"] = properties["msi_frequency"]
    elif target["family"] in ["l5"]:
        properties["hsi_frequency"] = 16_000_000
        properties["lsi_frequency"] = 32_000
        properties["msi_frequency"] = 4_000_000
        properties["boot_frequency"] = properties["msi_frequency"]
    else:
        properties["hsi_frequency"] = 16_000_000
        properties["lsi_frequency"] = 32_000
        properties["boot_frequency"] = properties["hsi_frequency"]

    # TODO: Move this data into the device files
    properties["usbprescaler"] = device.has_driver("usb") and target.family in ["f0", "f1", "f3"]
    properties["pllprediv"] = \
        (target["family"] in ["f0", "f3"] or (target["family"] == "f1" and target["name"] in ["00", "05", "07"]))
    properties["pllprediv2"] = False    # FIXME: not sure what value this should have
    properties["pll_hse_prediv2"] = target["family"] == "f1" and target["name"] in ["01", "02", "03"]
    properties["hsi48"] = \
        (target["family"] == "f0" and target["name"] in ["42", "48", "71", "72", "78", "91", "98"]) or \
        (target["family"] == "l4" and target["name"][0] not in ["7", "8"]) or \
        (target["family"] == "l5") or \
        (target["family"] == "u5")
    if target["family"] in ["l4", "l5"]:
        properties["hsi48_cr"] = "CRRCR"
    elif target["family"] in ["c0", "u5"]:
        properties["hsi48_cr"] = "CR"
    else:
        properties["hsi48_cr"] = "CR2"
    properties["pll_p"] = ((target["family"] == "l4" and target["name"] not in ["12", "22"]) or target["family"] == "g4")
    properties["overdrive"] = (target["family"] == "f7") or \
        ((target["family"] == "f4") and target["name"] in ["27", "29", "37", "39", "46", "69", "79"])
    properties["vos0_overdrive"] = (target["family"] == "h7") and \
        target["name"] in ["42", "43", "45", "47", "50", "53", "55", "57"]
    properties["has_r1mode"] = (target["family"] == "g4") or \
        (target["family"] == "l4" and target["name"][0] in ["p", "q", "r", "s"])
    properties["pllsai_p_usb"] = (target["family"] == "f7") or \
        ((target["family"] == "f4") and target["name"] in ["46", "69", "79"])

    if target.family in ["h7"]:
        if target.name in ["a3", "b0", "b3"]:
            properties["cfgr_prescaler"] = "CDCFGR1"
        else:
            properties["cfgr_prescaler"] = "D1CFGR"
    elif target.family in ["u5"]:
        properties["cfgr_prescaler"] = "CFGR2"
    else:
        properties["cfgr_prescaler"] = "CFGR"

    if target.family in ["h7"]:
        if target.name in ["a3", "b0", "b3"]:
            properties["cfgr2"] = "CDCFGR2"
        else:
            properties["cfgr2"] = "D2CFGR"
    elif target.family in ["u5"]:
        properties["cfgr2"] = "CFGR2"
    else:
        properties["cfgr2"] = "CFGR"

    if target.family in ["h7"]:
        if target.name in ["a3", "b0", "b3"]:
            properties["ccipr1"] = "CDCCIP1R"
        else:
            properties["ccipr1"] = "D2CCIP1R"
    elif target.family in ["l5", "u5"]:
        properties["ccipr1"] = "CCIPR1"
    else:
        properties["ccipr1"] = "CCIPR"

    properties["d1"] = ("CD" if target.name in ["a3", "b0", "b3"] else "D1") \
                        if target.family == "h7" else ""
    properties["d2"] = ("CD" if target.name in ["a3", "b0", "b3"] else "D2") \
                        if target.family == "h7" else ""
    properties["cfgr3"] = ("SRDCFGR" if target.name in ["a0", "a3", "b0", "b3"] else "D3CFGR")
    properties["d3"] = ("SRD" if target.name in ["a0", "a3", "b0", "b3"] else "D3")
    properties["bdcr"] = "CSR1" if target.family in ["c0"] else "CSR" if target.family in ["l0", "l1"] else "BDCR"
    properties["pll_ids"] = ["1", "2", "3"] if target.family in ["h7", "u5"] else [] if target.family in ["c0"] else [""]
    properties["has_smps"] = target["family"] == "h7" and (target["name"] in ["25", "35", "45", "47", "55", "57"] or \
        (target["name"] in ["30", "a3", "b0", "b3"] and target["variant"] == "q"))

    flash_latencies = {}
    for vcore in device.get_driver("flash")["latency"]:
        flash_latencies[int(vcore["vcore-min"])] = sorted([int(f["hclk-max"]) for f in vcore["wait-state"]])

    properties["table"] = flash_latencies
    env.substitutions = properties
    env.outbasepath = "modm/src/modm/platform/clock"

    env.template("rcc.cpp.in")
    env.template("rcc.hpp.in")

    all_peripherals = env.query(":cmsis:device:peripherals")
    rcc_map = env.query(":cmsis:device:rcc-map")
    rcc_enable = {}
    rcc_reset = {}

    for per, mode in rcc_map.items():
        nper = per
        # Fix CAN vs CAN1
        if "Can" in all_peripherals and per == "CAN1":
            per = "CAN"
            nper = "CAN1"
        # FDCAN
        if "Fdcan1" in all_peripherals and per == "FDCAN":
            per = "FDCAN1"
            nper = "FDCAN"
        # COMP12
        if "Comp1" in all_peripherals and per == "COMP12":
            per = "COMP1"
            nper = "COMP12"
        if "Comp2" in all_peripherals and per == "COMP12":
            per = "COMP2"
            nper = "COMP12"
        # DAC
        if "Dac1" in all_peripherals and per == "DAC":
            per = "DAC1"
            nper = "DAC"
        # DAC12
        if "Dac1" in all_peripherals and per == "DAC12":
            per = "DAC1"
            nper = "DAC12"
        if "Dac2" in all_peripherals and per == "DAC12":
            per = "DAC2"
            nper = "DAC12"
        if "Dac" in all_peripherals and per == "DAC1":
            per = "DAC"
            nper = "DAC1"
        # Fix ADC vs ADC1
        if "Adc1" in all_peripherals and per == "ADC":
            per = "ADC1"
            nper = "ADC"
        # Fix DSIHOST vs DSI
        if "Dsihost" in all_peripherals and per == "DSI":
            per = "Dsihost"
            nper = "DSI"
        if "Eth" in all_peripherals and per == "ETHMAC":
            per = "Eth"
        # Fix USBOTG OTG
        if target.family == "u5" and per == "OTG":
            per = "Usbotgfs"
        if ("Usbotgfs" in all_peripherals or "Usbotghs" in all_peripherals) and per.startswith("OTG"):
            if per == "OTGH": per = "OTGHS"
            per = "USB"+per
        if ("Usbotgfs" in all_peripherals or "Usbotghs" in all_peripherals) and per.startswith("USB"):
            per = per.replace("USB1", "USB").replace("USB2", "USB")
        if target.family == "l5" and per == "USBFS":
            per = "Usb"
        if per.capitalize() not in all_peripherals:
            continue
        if "EN" in mode:
            rcc_enable[per.capitalize()] = (nper, mode["EN"])
        if "RST" in mode:
            rcc_reset[per.capitalize()] = (nper, mode["RST"])

    env.substitutions.update({
        "rcc_enable": rcc_enable,
        "rcc_reset": rcc_reset,
    })
    env.template("rcc_impl.hpp.in")
